package bancosys.tec.datadigester.processor.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import jmine.tec.executionlog.ExecutionLogController;
import jmine.tec.utils.Tuple;
import jmine.tec.utils.register.Receiver;
import bancosys.tec.datadigester.processor.AbstractProcessor;
import bancosys.tec.datadigester.processor.Processor;
import bancosys.tec.datadigester.processor.ProcessorFactory;

/**
 * Default implementation of {@link ProcessorFactory}. There are two methods for configuring this factory.
 * <p>
 * Directly setting the backing map to be used (i.e. by Spring)
 * <p>
 * Registering onto the factory a collection of processors using the {@link jmine.tec.utils.register.Receiver} API (i.e. by Spring, again)
 * <p>
 * In case of a Spring scenario the second method has the advantage of not initializing the processors during the factory creation hence
 * allowing processors to eventually depend on the factory for delegating work to other processors. In this scenario the first method would
 * cause circular bean definitions resulting in a Spring startup failure.
 * 
 * @author lundberg
 */
public class SpringLoadedProcessorFactory implements ProcessorFactory, Receiver<Tuple<String, Processor>> {

    private Map<String, Processor> processorMap;

    private ExecutionLogController executionLogController;

    /**
     * Returns the processor associated with the fileCode. This method will return the same instances, not create a new one every time.
     * 
     * @param fileCode the fileCode
     * @return Processor the associated processor
     */
    public Processor createProcessor(String fileCode) {
        return this.getProcessorMap().get(fileCode);
    }

    /**
     * @param processorMap the processorMap to set
     */
    public void setProcessorMap(Map<String, Processor> processorMap) {
        this.processorMap = processorMap;
    }

    /**
     * @return the processorMap
     */
    public Map<String, Processor> getProcessorMap() {
        if (this.processorMap == null) {
            this.processorMap = new TreeMap<String, Processor>();
        }
        return this.processorMap;
    }

    /**
     * Define o executionLogController que será usado pelos processors para registrar os itens.
     * 
     * @param executionLogController o executionLogController.
     */
    public void setExecutionLogController(ExecutionLogController executionLogController) {
        this.executionLogController = executionLogController;
    }

    /**
     * Registers onto this factory the given list of processors.
     * 
     * @param registers a collection of tuples of KEY -> PROCESSOR
     */
    public void register(Collection<? extends Tuple<String, Processor>> registers) {
        for (Tuple<String, Processor> register : registers) {
            String key = register.getHead();
            Processor processor = register.getTail();

            // FIXME (piercio) Garante que todos os AbstractProcessors tenham um executionLogController, mesmo que não seja definido no
            // Spring.
            if (this.executionLogController != null && processor instanceof AbstractProcessor) {
                ((AbstractProcessor) processor).setExecutionLogController(this.executionLogController);
            }

            this.getProcessorMap().remove(key);
            this.getProcessorMap().put(key, processor);
        }
    }

    /**
     * Returns all available processor names
     * 
     * @return {@link List}
     */
    public List<? extends String> getAvailableProcessors() {
        return new ArrayList<String>(this.getProcessorMap().keySet());
    }

}
